﻿// -------------------------------------------------------------------------
//    @FileName         :    NFIDescTemplate.h
//    @Author           :    gaoyi
//    @Date             :    23-10-26
//    @Email			:    445267987@qq.com
//    @Module           :    NFIDescTemplate
//    @Desc             :    描述模板接口头文件，提供配置数据的模板化实现。
//                          该文件定义了描述模板接口类，包括模板化配置管理、
//                          数据加载和保存、索引管理、文件管理。
//                          主要功能包括提供配置数据的模板化实现、支持数据加载和保存、
//                          支持索引管理和查询、提供文件管理功能。
//                          描述模板接口是NFShmXFrame框架的配置管理模板组件，负责：
//                          - 配置数据的模板化实现
//                          - 数据加载和保存接口
//                          - 索引管理和查询功能
//                          - 文件管理和路径处理
//                          - 内存使用率计算
//                          - 跨平台文件路径支持
//
// -------------------------------------------------------------------------

#pragma once

#include "NFServerComm/NFServerCommon/NFIDescStore.h"
#include "NFComm/NFObjCommon/NFShmMgr.h"
#include "NFComm/NFShmStl/NFShmHashMap.h"
#include "NFComm/NFShmStl/NFShmHashMultiMap.h"
#include "NFComm/NFShmStl/NFShmHashMultiSet.h"
#include "NFComm/NFShmStl/NFShmHashSet.h"
#include "NFComm/NFShmStl/NFShmVector.h"
#include "NFComm/NFObjCommon/NFObjectTemplate.h"
#include "NFComm/NFCore/NFStringUtility.h"

#define NF_OBJECT_TEMPLATE_MACRO_USE_TEMPLATE

/**
 * 原本以为使用模版，会导致编译速度变慢。真实测试结果，发现不是这样的。
* 模板改成宏通常不会加快编译速度，有时甚至更慢！
原因：
    宏展开发生在预处理阶段
    每个使用宏的地方都完全展开
    代码膨胀，增加编译器处理量
    模板是编译时实例化
    相同类型只实例化一次
    编译器可以优化
    ccache 对模板更友好
    模板代码结构化，容易缓存
    宏展开后代码可能每次都不同
 * 使用方法类似：class NFCityWorldObject : public NF_OBJECT_TEMPLATE_MACRO(NFCityWorldObject, EOT_CITY_WORLD_OBJECT_ID, NFObject, public NFListNodeObjWithGlobalId<NFCityWorldObject>)
 * NFObject 一定要第一个被继承，不然在内存池校验的时候，GetObjId会校验不过
 */
#ifdef NF_OBJECT_TEMPLATE_MACRO_USE_TEMPLATE

#define NFI_DESC_TEMPLATE_MACRO(className, className_s, classType, DescNum) NFIDescTemplate<className, className_s, classType, DescNum> {

#define NFI_DESC_ARRAY_TEMPLATE_MACRO(className, className_s, classType, DescNum, ArrayFactor) NFIDescArrayTemplate<className, className_s, classType, DescNum, ArrayFactor> {

#else

#define NFI_DESC_TEMPLATE_MACRO(className, className_s, classType, DescNum) NF_OBJECT_GLOBAL_TEMPLATE_MACRO(className, classType, NFIDescStore)\
    int GetResNum() const override { return m_astDescVec.size(); }\
    int GetResMaxNum() const override { return m_astDescVec.max_size(); }\
    NFShmVector<className_s, DescNum>& GetResDesc() { return m_astDescVec; }\
    const NFShmVector<className_s, DescNum>& GetResDesc() const { return m_astDescVec; }\
    const className_s* GetDescByIndex(int index) const\
    {\
        CHECK_EXPR(index >= 0 && index < (int)m_astDescVec.size(), NULL, "index:{} size:{}", index, m_astDescVec.size());\
        return &m_astDescVec[index];\
    }\
    className_s* GetDescByIndex(int index)\
    {\
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDescByIndex(index));\
    }\
    const className_s* GetDesc(int64_t id) const\
    {\
        auto iter = m_astDescMap.find(id);\
        if (iter != m_astDescMap.end())\
        {\
            return GetDescByIndex(iter->second);\
        }\
        return nullptr;\
    }\
    className_s* GetDesc(int64_t id)\
    {\
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDesc(id));\
    }\
    int Initialize() override { return 0; }\
    int Reload(NFResDb* pDB) override\
    {\
        this->PrepareReload();\
        int iRetCode = this->Load(pDB);\
        return iRetCode;\
    }\
    std::string GetFileName() override\
    {\
        std::string strSubModuleName = typeid(className).name();\
        return GetFileNameImpl(strSubModuleName);\
    }\
    int CalcUseRatio() override\
    {\
        return m_astDescVec.size() * 100 / m_astDescVec.max_size();\
    }\
    int GetNeedWarnLine() const override\
    {\
        if (GetResMaxNum() >= 500)\
        {\
            return GetResMaxNum() - 50;\
        }\
        int iWarnLineNum = GetResMaxNum() * 0.95;\
        if (iWarnLineNum < 40)\
        {\
            iWarnLineNum = 40;\
        }\
        return iWarnLineNum;\
    }\
    int GetNeedWarnLineMin() const override\
    {\
        int iWarnLineNum = GetResMaxNum() * 0.2;\
        if (iWarnLineNum < 200)\
        {\
            iWarnLineNum = 0;\
        }\
        return iWarnLineNum;\
    }\
    int SaveDescStore() override\
    {\
        return 0;\
    }\
    virtual int InsertDescStore(const className_s& desc)\
    {\
        return 0;\
    }\
    virtual int DeleteDescStore(const className_s& desc)\
    {\
        return 0;\
    }\
    NFShmVector<className_s, DescNum> m_astDescVec;\
    NFShmHashMap<int64_t, int, DescNum> m_astDescMap;

#define NFI_DESC_ARRAY_TEMPLATE_MACRO(className, className_s, classType, DescNum, ArrayFactor) NF_OBJECT_GLOBAL_TEMPLATE_MACRO(className, classType, NFIDescStore)\
    int GetResNum() const override { return m_astDescVec.size(); }\
    int GetResMaxNum() const override { return m_astDescVec.max_size(); }\
    NFShmVector<className_s, DescNum>& GetResDesc() { return m_astDescVec; }\
    const NFShmVector<className_s, DescNum>& GetResDesc() const { return m_astDescVec; }\
    const className_s* GetDescByIndex(int index) const\
    {\
        CHECK_EXPR(index >= 0 && index < (int)m_astDescVec.size(), nullptr, "index:{} size:{}", index, m_astDescVec.size());\
        return &m_astDescVec[index];\
    }\
    className_s* GetDescByIndex(int index)\
    {\
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDescByIndex(index));\
    }\
    const className_s* GetDesc(int64_t id) const\
    {\
        int index = GetIndex(id);\
        if (index >= 0 && index < (int)m_astDescVec.size())\
        {\
            return &m_astDescVec[index];\
        }\
        return nullptr;\
    }\
    className_s* GetDesc(int64_t id)\
    {\
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDesc(id));\
    }\
    int GetIndex(int64_t id) const\
    {\
        int index = id - NFIDescStore::m_llMinId;\
        if (index >= 0 && index < (int)m_astDescIndex.size())\
        {\
            return m_astDescIndex[index];\
        }\
        return INVALID_ID;\
    }\
    int Initialize() override { return 0; }\
    int Reload(NFResDb* pDB) override\
    {\
        this->PrepareReload();\
        int iRetCode = this->Load(pDB);\
        return iRetCode;\
    }\
    std::string GetFileName() override\
    {\
        std::string strSubModuleName = typeid(className).name();\
        return GetFileNameImpl(strSubModuleName);\
    }\
    int CalcUseRatio() override\
    {\
        return m_astDescVec.size() * 100 / m_astDescVec.max_size();\
    }\
    int GetNeedWarnLine() const override\
    {\
        if (GetResMaxNum() >= 500)\
        {\
            return GetResMaxNum() - 50;\
        }\
        int iWarnLineNum = GetResMaxNum() * 0.95;\
        if (iWarnLineNum < 40)\
        {\
            iWarnLineNum = 40;\
        }\
        return iWarnLineNum;\
    }\
    int GetNeedWarnLineMin() const override\
    {\
        int iWarnLineNum = GetResMaxNum() * 0.2;\
        if (iWarnLineNum < 200)\
        {\
            iWarnLineNum = 0;\
        }\
        return iWarnLineNum;\
    }\
    int SaveDescStore() override\
    {\
        return 0;\
    }\
    virtual int InsertDescStore(const className_s& desc)\
    {\
        return 0;\
    }\
    virtual int DeleteDescStore(const className_s& desc)\
    {\
        return 0;\
    }\
    NFShmVector<className_s, DescNum> m_astDescVec;\
    NFShmVector<int, DescNum*ArrayFactor> m_astDescIndex;
#endif

/**
 * @brief 描述模板接口类
 * 
 * 该类是配置数据的模板化实现，提供了：
 * - 配置数据的模板化实现
 * - 数据加载和保存接口
 * - 索引管理和查询功能
 * - 文件管理和路径处理
 * 
 * @tparam className 类名
 * @tparam className_s 描述类名
 * @tparam classType 类类型
 * @tparam DescNum 描述数量
 * 
 * 主要功能：
 * - 提供配置数据的模板化实现
 * - 支持数据加载和保存接口
 * - 支持索引管理和查询功能
 * - 提供文件管理和路径处理
 * - 支持内存使用率计算
 * - 支持跨平台文件路径
 * 
 * 使用方式：
 * @code
 * class MyDescTemplate : public NFIDescTemplate<MyDescTemplate, MyDesc, EOT_MY_DESC, 1000> {
 * public:
 *     virtual int Load(NFResDb* pDB) override;
 *     virtual int SaveDescStore() override;
 * };
 * @endcode
 */
template <typename className, typename className_s, int classType, int DescNum>
class NFIDescTemplate : public NF_OBJECT_GLOBAL_TEMPLATE_MACRO(className, classType, NFIDescStore)

public:
    /**
     * @brief 构造函数
     * 
     * 根据共享内存管理器的创建模式，选择调用CreateInit或ResumeInit
     */
    NFIDescTemplate()
    {
        if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
        {
            CreateInit();
        }
        else
        {
            ResumeInit();
        }
    }

    /**
     * @brief 创建初始化
     * 
     * 在对象创建时调用，初始化描述模板
     * 
     * @return 初始化结果
     */
    int CreateInit()
    {
        return 0;
    }

    /**
     * @brief 恢复初始化
     * 
     * 在对象恢复时调用，恢复描述模板状态
     * 
     * @return 初始化结果
     */
    int ResumeInit()
    {
        return 0;
    }

public:
    /**
     * @brief 获取资源数量
     * 
     * 获取描述向量的数量
     * 
     * @return 资源数量
     */
    int GetResNum() const override { return m_astDescVec.size(); }

    /**
     * @brief 获取资源最大数量
     *
     * 获取配置数据的最大资源数量限制
     *
     * @return 最大资源数量
     */
    int GetResMaxNum() const override { return m_astDescVec.max_size(); }

    /**
     * @brief 获取资源描述向量（非const版本）
     * 
     * 获取描述向量的引用，可以修改
     * 
     * @return 描述向量引用
     */
    NFShmVector<className_s, DescNum>& GetResDesc() { return m_astDescVec; }

    /**
     * @brief 获取资源描述向量（const版本）
     * 
     * 获取描述向量的常量引用，只读
     * 
     * @return 描述向量常量引用
     */
    const NFShmVector<className_s, DescNum>& GetResDesc() const { return m_astDescVec; }

    /**
     * @brief 根据索引获取描述（const版本）
     * 
     * 根据索引获取描述对象，只读
     * 
     * @param index 索引
     * @return 描述对象指针，如果索引无效返回nullptr
     */
    const className_s* GetDescByIndex(int index) const
    {
        CHECK_EXPR(index >= 0 && index < (int)m_astDescVec.size(), NULL, "index:{} size:{}", index, m_astDescVec.size());
        return &m_astDescVec[index];
    }

    /**
     * @brief 根据索引获取描述（非const版本）
     * 
     * 根据索引获取描述对象，可以修改
     * 
     * @param index 索引
     * @return 描述对象指针，如果索引无效返回nullptr
     */
    className_s* GetDescByIndex(int index)
    {
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDescByIndex(index));
    }

    /**
     * @brief 根据ID获取描述（const版本）
     * 
     * 根据ID从映射表中查找并获取描述对象，只读
     * 
     * @param id 描述ID
     * @return 描述对象指针，如果未找到返回nullptr
     */
    const className_s* GetDesc(int64_t id) const
    {
        auto iter = m_astDescMap.find(id);
        if (iter != m_astDescMap.end())
        {
            return GetDescByIndex(iter->second);
        }
        return nullptr;
    }

    /**
     * @brief 根据ID获取描述（非const版本）
     * 
     * 根据ID从映射表中查找并获取描述对象，可以修改
     * 
     * @param id 描述ID
     * @return 描述对象指针，如果未找到返回nullptr
     */
    className_s* GetDesc(int64_t id)
    {
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDesc(id));
    }

public:
    /**
     * @brief 初始化
     * 
     * 初始化描述模板
     * 
     * @return 初始化结果
     */
    int Initialize() override { return 0; }

    /**
     * @brief 加载数据
     * 
     * 从数据库加载描述数据
     * 
     * @param pDB 数据库对象
     * @return 加载结果
     */
    int Load(NFResDb* pDB) override { return 0; }

    /**
     * @brief 加载数据库数据
     * 
     * 从数据库加载描述数据
     * 
     * @param pDB 数据库对象
     * @return 加载结果
     */
    int LoadDB(NFResDb* pDB) override { return 0; }

    /**
     * @brief 检查所有数据加载完成
     * 
     * 检查所有描述数据是否已经加载完成
     * 
     * @return 检查结果
     */
    int CheckWhenAllDataLoaded() override { return 0; }

    /**
     * @brief 重新加载
     * 
     * 重新加载描述数据
     * 
     * @param pDB 数据库对象
     * @return 重新加载结果
     */
    int Reload(NFResDb* pDB) override
    {
        this->PrepareReload();
        int iRetCode = this->Load(pDB);
        return iRetCode;
    }

    /**
     * @brief 获取文件名
     * 
     * 根据类名生成文件名
     * 
     * @return 文件名
     */
    std::string GetFileName() override
    {
        std::string strSubModuleName = typeid(className).name();
        return NFIDescStore::GetFileNameImpl(strSubModuleName);
    }

    int CalcUseRatio() override
    {
        return m_astDescVec.size() * 100 / m_astDescVec.max_size();
    }

    /**
     * @brief 获取需要警告的行数 表格行数>此值会输出告警
     *
     * 获取配置文件中需要发出警告的最大行数阈值
     * 当配置文件行数超过此值时，系统会发出警告
     *
     * @return 需要警告的行数阈值
     */
    int GetNeedWarnLine() const override
    {
        if (GetResMaxNum() >= 500) // 行数太多 统一差100行时再告警(百分比的话剩余还很多就会报警了)
        {
            return GetResMaxNum() - 50;
        }

        int iWarnLineNum = GetResMaxNum() * 0.95; // 默认实现%95告警
        if (iWarnLineNum < 40) // 太少行的表 没必要报警,不是频繁改动的
        {
            iWarnLineNum = 40;
        }
        return iWarnLineNum;
    }

    /**
     * @brief 获取需要警告的最小行数 表格行数<此值会输出告警  返回0不告警
     *
     * 获取配置文件中需要发出警告的最小行数阈值
     * 当配置文件行数小于此值时，系统会发出警告
     *
     * @return 需要警告的最小行数阈值
     */
    int GetNeedWarnLineMin() const override
    {
        int iWarnLineNum = GetResMaxNum() * 0.2; // 默认实现%20告警
        if (iWarnLineNum < 200) // 太少行的表 没必要报警
        {
            iWarnLineNum = 0;
        }
        return iWarnLineNum;
    }

    int SaveDescStore() override
    {
        return 0;
    }

    virtual int InsertDescStore(const className_s& desc)
    {
        return 0;
    }

    virtual int DeleteDescStore(const className_s& desc)
    {
        return 0;
    }

protected:
    NFShmVector<className_s, DescNum> m_astDescVec;
    NFShmHashMap<int64_t, int, DescNum> m_astDescMap;
};

template <typename className, typename className_s, int classType, int DescNum, int ArrayFactor>
class NFIDescArrayTemplate : public NF_OBJECT_GLOBAL_TEMPLATE_MACRO(className, classType, NFIDescStore)

public:
    /**
     * @brief 构造函数
     *
     * 根据共享内存管理器的创建模式，选择调用CreateInit或ResumeInit
     */
    NFIDescArrayTemplate()
    {
        if (EN_OBJ_MODE_INIT == NFShmMgr::Instance()->GetCreateMode())
        {
            CreateInit();
        }
        else
        {
            ResumeInit();
        }
    }

    /**
     * @brief 创建初始化
     *
     * 在对象创建时调用，初始化描述模板
     *
     * @return 初始化结果
     */
    int CreateInit()
    {
        return 0;
    }

    /**
     * @brief 恢复初始化
     *
     * 在对象恢复时调用，恢复描述模板状态
     *
     * @return 初始化结果
     */
    int ResumeInit()
    {
        return 0;
    }

public:
    /**
     * @brief 获取资源数量
     *
     * 获取描述向量的数量
     *
     * @return 资源数量
     */
    int GetResNum() const override { return m_astDescVec.size(); }

    /**
    * @brief 获取资源最大数量
    *
    * 获取配置数据的最大资源数量限制
    *
    * @return 最大资源数量
    */
    int GetResMaxNum() const override { return m_astDescVec.max_size(); }

    /**
     * @brief 获取资源描述向量（非const版本）
     *
     * 获取描述向量的引用，可以修改
     *
     * @return 描述向量引用
     */
    NFShmVector<className_s, DescNum>& GetResDesc() { return m_astDescVec; }

    /**
     * @brief 获取资源描述向量（const版本）
     *
     * 获取描述向量的常量引用，只读
     *
     * @return 描述向量常量引用
     */
    const NFShmVector<className_s, DescNum>& GetResDesc() const { return m_astDescVec; }

    /**
     * @brief 根据索引获取描述（const版本）
     *
     * 根据索引获取描述对象，只读
     *
     * @param index 索引
     * @return 描述对象指针，如果索引无效返回nullptr
     */
    const className_s* GetDescByIndex(int index) const
    {
        CHECK_EXPR(index >= 0 && index < (int)m_astDescVec.size(), nullptr, "index:{} size:{}", index, m_astDescVec.size());
        return &m_astDescVec[index];
    }

    /**
     * @brief 根据索引获取描述（非const版本）
     *
     * 根据索引获取描述对象，可以修改
     *
     * @param index 索引
     * @return 描述对象指针，如果索引无效返回nullptr
     */
    className_s* GetDescByIndex(int index)
    {
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDescByIndex(index));
    }

    /**
     * @brief 根据ID获取描述（const版本）
     *
     * 根据ID从映射表中查找并获取描述对象，只读
     *
     * @param id 描述ID
     * @return 描述对象指针，如果未找到返回nullptr
     */
    const className_s* GetDesc(int64_t id) const
    {
        int index = GetIndex(id);
        if (index >= 0 && index < (int)m_astDescVec.size())
        {
            return &m_astDescVec[index];
        }
        return nullptr;
    }

    /**
     * @brief 根据ID获取描述（非const版本）
     *
     * 根据ID从映射表中查找并获取描述对象，可以修改
     *
     * @param id 描述ID
     * @return 描述对象指针，如果未找到返回nullptr
     */
    className_s* GetDesc(int64_t id)
    {
        return const_cast<className_s*>((static_cast<const className*>(this))->GetDesc(id));
    }

    int GetIndex(int64_t id) const
    {
        int index = id - NFIDescStore::m_llMinId;
        if (index >= 0 && index < (int)m_astDescIndex.size())
        {
            return m_astDescIndex[index];
        }
        return INVALID_ID;
    }

public:
    /**
     * @brief 初始化
     *
     * 初始化描述模板
     *
     * @return 初始化结果
     */
    int Initialize() override { return 0; }

    /**
     * @brief 加载数据
     *
     * 从数据库加载描述数据
     *
     * @param pDB 数据库对象
     * @return 加载结果
     */
    int Load(NFResDb* pDB) override { return 0; }

    /**
     * @brief 加载数据库数据
     *
     * 从数据库加载描述数据
     *
     * @param pDB 数据库对象
     * @return 加载结果
     */
    int LoadDB(NFResDb* pDB) override { return 0; }

    /**
     * @brief 检查所有数据加载完成
     *
     * 检查所有描述数据是否已经加载完成
     *
     * @return 检查结果
     */
    int CheckWhenAllDataLoaded() override { return 0; }

    /**
     * @brief 重新加载
     *
     * 重新加载描述数据
     *
     * @param pDB 数据库对象
     * @return 重新加载结果
     */
    int Reload(NFResDb* pDB) override
    {
        this->PrepareReload();
        int iRetCode = this->Load(pDB);
        return iRetCode;
    }

    /**
     * @brief 获取文件名
     *
     * 根据类名生成文件名
     *
     * @return 文件名
     */
    std::string GetFileName() override
    {
        std::string strSubModuleName = typeid(className).name();
        return  NFIDescStore::GetFileNameImpl(strSubModuleName);
    }

    int CalcUseRatio() override
    {
        return m_astDescVec.size() * 100 / m_astDescVec.max_size();
    }

    /**
     * @brief 获取需要警告的行数 表格行数>此值会输出告警
     *
     * 获取配置文件中需要发出警告的最大行数阈值
     * 当配置文件行数超过此值时，系统会发出警告
     *
     * @return 需要警告的行数阈值
     */
    int GetNeedWarnLine() const override
    {
        if (GetResMaxNum() >= 500) // 行数太多 统一差100行时再告警(百分比的话剩余还很多就会报警了)
        {
            return GetResMaxNum() - 50;
        }

        int iWarnLineNum = GetResMaxNum() * 0.95; // 默认实现%95告警
        if (iWarnLineNum < 40) // 太少行的表 没必要报警,不是频繁改动的
        {
            iWarnLineNum = 40;
        }
        return iWarnLineNum;
    }

    /**
     * @brief 获取需要警告的最小行数 表格行数<此值会输出告警  返回0不告警
     *
     * 获取配置文件中需要发出警告的最小行数阈值
     * 当配置文件行数小于此值时，系统会发出警告
     *
     * @return 需要警告的最小行数阈值
     */
    int GetNeedWarnLineMin() const override
    {
        int iWarnLineNum = GetResMaxNum() * 0.2; // 默认实现%20告警
        if (iWarnLineNum < 200) // 太少行的表 没必要报警
        {
            iWarnLineNum = 0;
        }
        return iWarnLineNum;
    }

    int SaveDescStore() override
    {
        return 0;
    }

    virtual int InsertDescStore(const className_s& desc)
    {
        return 0;
    }

    virtual int DeleteDescStore(const className_s& desc)
    {
        return 0;
    }

protected:
    NFShmVector<className_s, DescNum> m_astDescVec;
    NFShmVector<int, DescNum * ArrayFactor> m_astDescIndex;
};
